/**
 * @file kprintf.c
 * @provides kputc, kgetc, kungetc, kcheckc, kprintf
 *
 * $Id: kprintf.c 175 2008-01-30 01:18:27Z brylow $
 *
 * Modified by:
 * Christopher Gozdziewski
 * and
 * Andrew Kirkham
 */
/* Embedded XINU, Copyright (C) 2007.  All rights reserved. */

#include <kernel.h>
#include <stdarg.h>
#include <device.h>
#include <stdio.h>
#include <uart.h>

#define UNGETMAX 10  /* Can un-get at most 10 characters. */

static unsigned char ungetArray[UNGETMAX];
static int arrayCounter = -1;

/**
 * kputc - perform a synchronous kernel write to the serial console
 * @param *pdev device pointer (unused for the synchronous driver)
 * @param c character to write
 * @return c on success, SYSERR on failure.
 */
syscall kputc(device *pdev, unsigned char c)
{
	volatile struct uart_csreg *pucsr = (struct uart_csreg *)0xB8000300;
		
	while(1)
	{
		if ((pucsr -> lsr & UART_LSR_THRE) == UART_LSR_THRE) 
		{
			break;
		}
	}
	if (0 != (int)c){
		pucsr -> buffer = c;	
		return c;
	}
	else {
		return SYSERR;
	}
}

/**
 * kcheckc - check to see if a character is available.
 * @return true if a character is available, false otherwise.
 */
syscall kcheckc(void)
{
	volatile struct uart_csreg *pucsr = (struct uart_csreg *)0xB8000300;

	if (arrayCounter != -1 )
	{
		return 1;
	}
	else if ((pucsr -> lsr &UART_LSR_DR) == UART_LSR_DR)
	{
		return 1;
	}

	return 0;  // false
}

/**
 * kgetc - perform a synchronous kernel read from the serial console,
 *         or from the local buffer if there are "ungotten" characters.
 * @return c on success, SYSERR on failure.
 */
syscall kgetc(void)
{
	volatile struct uart_csreg *pucsr = (struct uart_csreg *)0xB8000300;

	if (-1 != arrayCounter)
	{
		char c  = ungetArray[arrayCounter];
		arrayCounter--;	
		return c; 
	}
	else
	{
		while (1)
		{
			if ((pucsr -> lsr &UART_LSR_DR) == UART_LSR_DR)
				break;
		}					
				
		return pucsr -> buffer;			
	}
	return SYSERR; 		
}

/**
 * kungetc - put a serial character "back" into a local buffer.
 * @param c character to unget.
 * @return c on success, SYSERR on failure.
 */
syscall kungetc(unsigned char c)
{
	if (arrayCounter != (UNGETMAX - 1))
	{
		arrayCounter++;
		ungetArray[arrayCounter] = c;
		return c;
	}	
	else
	{
		kprintf ("Unget Buffer is full!");
		return SYSERR;
	}
}

/**
 * kprintf - kernel printf: formatted, unbuffered output to CONSOLE
 * @param *fmt pointer to string being printed
 * @return OK on success
 */
syscall kprintf(char *fmt, ...)
{
	va_list ap;

	va_start(ap, fmt);
	_doprnt(fmt, ap, (int (*)(int, int))kputc, 0);
	va_end(ap);
	return OK;
}

